package com.mspbots.web.trigger;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.mspbots.web.trigger.model.Tenant;
import com.mspbots.web.trigger.model.TimeEntry;
import com.mspbots.web.trigger.model.TriggerEvent;
import com.mspbots.web.trigger.model.UserAttendance;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitMessagingTemplate;
import org.springframework.data.r2dbc.core.DatabaseClient;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.time.LocalDateTime;

import static com.mspbots.common.TriggerEvent.USER_IDLE;


@Slf4j
@Service
@RequiredArgsConstructor
public class UserAttendanceEvent {

    private final DatabaseClient databaseClient;
    private final RabbitMessagingTemplate messagingTemplate;
    private final ObjectMapper objectMapper;

    @Scheduled(fixedDelayString ="${mspbots.fixedDelay.userCheck}")
    public void produce(){
        Long time  = System.currentTimeMillis();
        log.info("userAttendance:.test.jvm...start time:{}",time);
        this.databaseClient.execute("select t.id,tc.keyword_value as workType " +
                "from tenant t " +
                "left join tenant_configuration tc on t.id = tc.tenant_id and tc.keyword = 'mspbots.cw.ticket.working' " +
                "where t.status_id  = 1")
                .as(Tenant.class)
                .fetch()
                .all()
                .filter(tenant -> tenant.getId() > 0)
                .map(tenant -> {
                    if(ObjectUtils.isEmpty(tenant.getWorkType())){
                        tenant.setWorkType("Progress");
                    }
                    return tenant;
                })
                .doOnNext(tenant -> {
                    log.debug("start time :{} tenant id :{} take time: {}",time,tenant.getId(),System.currentTimeMillis()-time);
                })
                .flatMap(tenant ->
                        this.databaseClient.execute("select u.id,u.tenant_id,u.username,u.enabled, u.email,u.first_name, u.last_name,u.manager_user_id,u.created_time,u. modified_time, " +
                                "tm.teams_user_id,tm.situation, " +
                                "tu.id as tenantUserId,tu.cw_uid,tu.identifier,tu.manager as cw_manager_user_id,CASE WHEN taa.sub_action is null THEN taa.action ELSE taa.sub_action END as action " +
                                "from users u,tenant_teams_user tm,tenant_user tu ,(select * from ( " +
                                "SELECT *,rank() over(partition by user_id order by id desc) rk from tenant_attendance where tenant_id = :tenantId ) ta " +
                                "where ta.rk = 1 and (action = 'in' or sub_action = 'back') ) taa " +
                                "where u.tenant_id = :tenantId " +
                                "and u.enabled = true and u.removed is null " +
                                "and u.tenant_id = tm.tenant_id and  upper(u.username) = upper(tm.teams_mail) " +
                                "and tm.tenant_id = tu.tenant_id and  upper(tm.teams_mail) = upper(tu.email) " +
                                "and u.tenant_id = taa.tenant_id and u.id = taa.user_id")
                                .bind("tenantId",tenant.getId())
                                .as(UserAttendance.class)
                                .fetch()
                                .all()
                                .map(userAttendance -> {
                                    log.debug("userAttendance:users:id:"+userAttendance);
                                    userAttendance.setWorkType(tenant.getWorkType());
                                    return userAttendance;
                                })
                )
                .doOnNext(user -> {
                    log.debug("start time :{} user id:{} take time: {}",time,user.getId(),System.currentTimeMillis()-time);
                })
                .flatMap(userAttendance ->
                        this.databaseClient.execute("SELECT count(*) from tenant_tickets " +
                                " where tenant_id = :tenantId and resources ilike CONCAT('%',:identifier,'%') and status ilike CONCAT('%',:workType,'%') ")
                                .bind("tenantId",userAttendance.getTenantId())
                                .bind("identifier",userAttendance.getIdentifier())
                                .bind("workType",userAttendance.getWorkType())
                                .as(Long.class)
                                .fetch()
                                .one()
                                .filter(count -> count == 0)
                                .map(count -> {
                                    log.debug("userAttendance:tenant_tickets:");
                                    userAttendance.setIsIdle(count == 0);
                                    return userAttendance;
                                })
                )
                .doOnNext(tickets -> {
                    log.debug("start time :{} tenant_tickets id:{} take time: {}",time,tickets.getId(),System.currentTimeMillis()-time);
                })
                .flatMap(userAttendance ->
                        this.databaseClient.execute("SELECT * from tenant_time_entry where tenant_id = :tenantId and cw_uid = :cwUid and end_date is not null " +
                                "ORDER BY end_date desc LIMIT 1")
                                .bind("tenantId",userAttendance.getTenantId())
                                .bind("cwUid",userAttendance.getCwUid())
                                .as(TimeEntry.class)
                                .fetch()
                                .one()
                                .filter(timeEntry -> timeEntry.getEndDate().isBefore(LocalDateTime.now()))
                                .map(timeEntry -> {
                                    userAttendance.setPayload(timeEntry.getExtend());
                                    return userAttendance;
                                })
                )
                .doOnNext(timeEntry -> {
                    log.debug("start time :{} tenant_time_entry:id:{} take time: {}",time,timeEntry.getId(),System.currentTimeMillis()-time);
                })
                .subscribe(userAttendance -> {
                        log.debug("userAttendance:{}",this.objectMapper.convertValue(userAttendance, JsonNode.class));
                        TriggerEvent triggerEvent = new TriggerEvent();
                        triggerEvent.setTenantId(Long.valueOf(userAttendance.getTenantId()));
                        triggerEvent.setEventName(USER_IDLE.getEvent());
                        triggerEvent.setScope(USER_IDLE.getScope());
                        triggerEvent.setPayload(userAttendance.getPayload());
                        ObjectNode userinfo = this.objectMapper.createObjectNode();
                        userinfo.put("userId",userAttendance.getId());
                        userinfo.put("teamsUserId",userAttendance.getTeamsUserId());
                        userinfo.put("username",userAttendance.getUsername());
                        userinfo.put("identifier",userAttendance.getIdentifier());
                        userinfo.put("tenantId",userAttendance.getTenantId());
                        userinfo.put("firstName",userAttendance.getFirstName());
                        userinfo.put("tenantUserId",userAttendance.getTenantUserId());
                        userinfo.put("managerUserId",userAttendance.getManagerUserId());
                        userinfo.put("cwManagerUserId",userAttendance.getCwManagerUserId());
                        log.debug("userAttendance:cwManagerUserId:{}",userAttendance.getCwManagerUserId());
                        JsonNode userinfoNode = this.objectMapper.convertValue(userinfo,JsonNode.class);
                        log.debug("userAttendance user info:{}",userinfoNode);
                        triggerEvent.setUserInfo(userinfoNode);
                        this.messagingTemplate.convertAndSend("msp.trigger.event", "", triggerEvent);
                        log.info("userAttendance:...end:: start time:{},tenantId:{},userId:{},time entry id:{}",time,userAttendance.getTenantId(),userAttendance.getId(),userAttendance.getPayload().get("id").asText());
                },error ->{
                    log.error("userAttendance:error:{}",error);
                });

    }

}
